#nullable disable

using Crux.Core.Data;

namespace Crux.Core.NHibernate;
/// <inheritdoc/>
public abstract class NHibernateContext
    : ISession,
    IDisposableObservable
{
    #region private fields
    internal ISession Session { get; init; }
    #endregion private fields
    #region implements ISession
    /// <inheritdoc/>
    public FlushMode FlushMode
    {
        get => Session.FlushMode;
        set => Session.FlushMode = value;
    }
    /// <inheritdoc/>
    public CacheMode CacheMode
    {
        get => Session.CacheMode;
        set => Session.CacheMode = value;
    }
    /// <inheritdoc/>
    public ISessionFactory SessionFactory => Session.SessionFactory;
    /// <inheritdoc/>
    public DbConnection Connection => Session.Connection;
    /// <inheritdoc/>
    public bool IsOpen => Session.IsOpen;
    /// <inheritdoc/>
    public bool IsConnected => Session.IsConnected;
    /// <inheritdoc/>
    public bool DefaultReadOnly
    {
        get => Session.DefaultReadOnly;
        set => Session.DefaultReadOnly = value;
    }
    /// <inheritdoc/>
    [Obsolete]
    public ITransaction Transaction => Session.Transaction;
    /// <inheritdoc/>
    public ISessionStatistics Statistics
        => Session.Statistics;
    /// <inheritdoc/>
    public ITransaction GetCurrentTransaction()
        => Session.GetCurrentTransaction();
    /// <inheritdoc/>
    public IUniqueIdentifierGenerator generator;
    #endregion implements ISession

    #region .ctor
    /// <inheritdoc/>
    public NHibernateContext(
        ISession session,
        IUniqueIdentifierGenerator generator = null)
    {
        Session = session
            ?? throw new ArgumentNullException(nameof(session));
        if (!Session.IsConnected)
            Session.Reconnect();
        this.generator = generator
            ?? new UniqueIdentifierGenerator();
    }
    #endregion .ctor


    #region implements ISession
    /// <inheritdoc/>
    public async Task FlushAsync(CancellationToken cancellationToken = default)
    {
        try
        {
            await Session.FlushAsync(cancellationToken).ConfigureAwait(false);
        }
        catch (StaleObjectStateException e)
        {
            throw new ConflictException("更新冲突", e);
        }
    }
    /// <inheritdoc/>
    public void Flush()
    {
        try
        {
            Session.Flush();
        }
        catch (StaleObjectStateException e)
        {
            throw new ConflictException("更新冲突", e);
        }
    }
    /// <inheritdoc/>
    public Task<bool> IsDirtyAsync(CancellationToken cancellationToken = default)
        => Session.IsDirtyAsync(cancellationToken);
    /// <inheritdoc/>
    public Task EvictAsync(object obj, CancellationToken cancellationToken = default)
        => Session.EvictAsync(obj, cancellationToken);
    /// <inheritdoc/>
    public Task<object> LoadAsync(Type theType, object id, LockMode lockMode, CancellationToken cancellationToken = default)
        => Session.LoadAsync(theType, id, lockMode, cancellationToken);
    /// <inheritdoc/>
    public Task<object> LoadAsync(string entityName, object id, LockMode lockMode, CancellationToken cancellationToken = default)
        => Session.LoadAsync(entityName, id, lockMode, cancellationToken);
    /// <inheritdoc/>
    public Task<object> LoadAsync(Type theType, object id, CancellationToken cancellationToken = default)
        => Session.LoadAsync(theType, id, cancellationToken);
    /// <inheritdoc/>
    public Task<T> LoadAsync<T>(object id, LockMode lockMode, CancellationToken cancellationToken = default)
        => Session.LoadAsync<T>(id, lockMode, cancellationToken);
    /// <inheritdoc/>
    public Task<T> LoadAsync<T>(object id, CancellationToken cancellationToken = default)
        => Session.LoadAsync<T>(id, cancellationToken);
    /// <inheritdoc/>
    public Task<object> LoadAsync(string entityName, object id, CancellationToken cancellationToken = default)
        => Session.LoadAsync(entityName, id, cancellationToken);
    /// <inheritdoc/>
    public Task LoadAsync(object obj, object id, CancellationToken cancellationToken = default)
        => Session.LoadAsync(obj, id, cancellationToken);
    /// <inheritdoc/>
    public Task ReplicateAsync(object obj, ReplicationMode replicationMode, CancellationToken cancellationToken = default)
        => Session.ReplicateAsync(obj, replicationMode, cancellationToken);
    /// <inheritdoc/>
    public Task ReplicateAsync(string entityName, object obj, ReplicationMode replicationMode, CancellationToken cancellationToken = default)
        => Session.ReplicateAsync(entityName, obj, replicationMode, cancellationToken);
    /// <inheritdoc/>
    public Task<object> SaveAsync(object obj, CancellationToken cancellationToken = default)
        => Session.SaveAsync(obj, cancellationToken);
    /// <inheritdoc/>
    public Task SaveAsync(object obj, object id, CancellationToken cancellationToken = default)
        => Session.SaveAsync(obj, id, cancellationToken);
    /// <inheritdoc/>
    public Task<object> SaveAsync(string entityName, object obj, CancellationToken cancellationToken = default)
        => Session.SaveAsync(entityName, obj, cancellationToken);
    /// <inheritdoc/>
    public Task SaveAsync(string entityName, object obj, object id, CancellationToken cancellationToken = default)
        => Session.SaveAsync(entityName, obj, id, cancellationToken);
    /// <inheritdoc/>
    public Task SaveOrUpdateAsync(object obj, CancellationToken cancellationToken = default)
        => Session.SaveOrUpdateAsync(obj, cancellationToken);
    /// <inheritdoc/>
    public Task SaveOrUpdateAsync(string entityName, object obj, CancellationToken cancellationToken = default)
        => Session.SaveOrUpdateAsync(entityName, obj, cancellationToken);
    /// <inheritdoc/>
    public Task SaveOrUpdateAsync(string entityName, object obj, object id, CancellationToken cancellationToken = default)
        => Session.SaveOrUpdateAsync(entityName, obj, id, cancellationToken);
    /// <inheritdoc/>
    public Task UpdateAsync(object obj, CancellationToken cancellationToken = default)
        => Session.UpdateAsync(obj, cancellationToken);
    /// <inheritdoc/>
    public Task UpdateAsync(object obj, object id, CancellationToken cancellationToken = default)
        => Session.UpdateAsync(obj, id, cancellationToken);
    /// <inheritdoc/>
    public Task UpdateAsync(string entityName, object obj, CancellationToken cancellationToken = default)
        => Session.UpdateAsync(entityName, obj, cancellationToken);
    /// <inheritdoc/>
    public Task UpdateAsync(string entityName, object obj, object id, CancellationToken cancellationToken = default)
        => Session.UpdateAsync(entityName, obj, id, cancellationToken);
    /// <inheritdoc/>
    public Task<object> MergeAsync(object obj, CancellationToken cancellationToken = default)
        => Session.MergeAsync(obj, cancellationToken);
    /// <inheritdoc/>
    public Task<object> MergeAsync(string entityName, object obj, CancellationToken cancellationToken = default)
        => Session.MergeAsync(entityName, obj, cancellationToken);
    /// <inheritdoc/>
    public Task<T> MergeAsync<T>(T entity, CancellationToken cancellationToken = default) where T : class
        => Session.MergeAsync(entity, cancellationToken);
    /// <inheritdoc/>
    public Task<T> MergeAsync<T>(string entityName, T entity, CancellationToken cancellationToken = default) where T : class
        => Session.MergeAsync(entityName, entity, cancellationToken);
    /// <inheritdoc/>
    public Task PersistAsync(object obj, CancellationToken cancellationToken = default)
    {
        if (obj is IGenericEntity<Guid> guidEntity)
        {
            guidEntity.Id = generator.Generate<Guid>();
        }
        else if (obj is IGenericEntity<long> longEntity)
        {
            longEntity.Id = generator.Generate<long>();
        }
        else if (obj is IGenericEntity<string> stringEntity)
        {
            stringEntity.Id = generator.Generate<string>();
        }

        return Session.PersistAsync(obj, cancellationToken);
    }
    /// <inheritdoc/>
    public Task PersistAsync(string entityName, object obj, CancellationToken cancellationToken = default)
    {
        if (obj is IGenericEntity<Guid> guidEntity)
        {
            guidEntity.Id = generator.Generate<Guid>();
        }
        else if (obj is IGenericEntity<long> longEntity)
        {
            longEntity.Id = generator.Generate<long>();
        }
        else if (obj is IGenericEntity<string> stringEntity)
        {
            stringEntity.Id = generator.Generate<string>();
        }

        return Session.PersistAsync(entityName, obj, cancellationToken);
    }
    /// <inheritdoc/>
    public async Task DeleteAsync(object obj, CancellationToken cancellationToken = default)
    {
        if (obj is ISoftDelete softDelete)
        {
            softDelete.Deleted = true;

            await Session.MergeAsync(softDelete, cancellationToken).ConfigureAwait(false);
        }
        else
        {
            await Session.DeleteAsync(obj, cancellationToken).ConfigureAwait(false);
        }
    }
    /// <inheritdoc/>
    public async Task DeleteAsync(string entityName, object obj, CancellationToken cancellationToken = default)
    {
        if (obj is ISoftDelete softDelete)
        {
            softDelete.Deleted = true;

            await Session.MergeAsync(entityName, softDelete, cancellationToken).ConfigureAwait(false);
        }
        else
        {
            await Session.DeleteAsync(entityName, obj, cancellationToken).ConfigureAwait(false);
        }
    }
    /// <inheritdoc/>
    public Task<int> DeleteAsync(string query, CancellationToken cancellationToken = default)
        => Session.DeleteAsync(query, cancellationToken);
    /// <inheritdoc/>
    public Task<int> DeleteAsync(string query, object value, IType type, CancellationToken cancellationToken = default)
        => Session.DeleteAsync(query, value, type, cancellationToken);
    /// <inheritdoc/>
    public Task<int> DeleteAsync(string query, object[] values, IType[] types, CancellationToken cancellationToken = default)
        => Session.DeleteAsync(query, values, types, cancellationToken);
    /// <inheritdoc/>
    public Task LockAsync(object obj, LockMode lockMode, CancellationToken cancellationToken = default)
        => Session.LockAsync(obj, lockMode, cancellationToken);
    /// <inheritdoc/>
    public Task LockAsync(string entityName, object obj, LockMode lockMode, CancellationToken cancellationToken = default)
        => Session.LockAsync(entityName, obj, lockMode, cancellationToken);
    /// <inheritdoc/>
    public Task RefreshAsync(object obj, CancellationToken cancellationToken = default)
        => Session.RefreshAsync(obj, cancellationToken);
    /// <inheritdoc/>
    public Task RefreshAsync(object obj, LockMode lockMode, CancellationToken cancellationToken = default)
        => Session.RefreshAsync(obj, lockMode, cancellationToken);
    /// <inheritdoc/>
    public Task<IQuery> CreateFilterAsync(object collection, string queryString, CancellationToken cancellationToken = default)
        => Session.CreateFilterAsync(collection, queryString, cancellationToken);
    /// <inheritdoc/>
    public Task<object> GetAsync(Type clazz, object id, CancellationToken cancellationToken = default)
        => Session.GetAsync(clazz, id, cancellationToken);
    /// <inheritdoc/>
    public Task<object> GetAsync(Type clazz, object id, LockMode lockMode, CancellationToken cancellationToken = default)
        => Session.GetAsync(clazz, id, lockMode, cancellationToken);
    /// <inheritdoc/>
    public Task<object> GetAsync(string entityName, object id, CancellationToken cancellationToken = default)
        => Session.GetAsync(entityName, id, cancellationToken);
    /// <inheritdoc/>
    public Task<T> GetAsync<T>(object id, CancellationToken cancellationToken = default)
        => Session.GetAsync<T>(id, cancellationToken);
    /// <inheritdoc/>
    public Task<T> GetAsync<T>(object id, LockMode lockMode, CancellationToken cancellationToken = default)
        => Session.GetAsync<T>(id, lockMode, cancellationToken);
    /// <inheritdoc/>
    public Task<string> GetEntityNameAsync(object obj, CancellationToken cancellationToken = default)
        => Session.GetEntityNameAsync(obj, cancellationToken);
    /// <inheritdoc/>
    public ISharedSessionBuilder SessionWithOptions()
        => Session.SessionWithOptions();
    /// <inheritdoc/>
    public DbConnection Disconnect()
        => Session.Disconnect();
    /// <inheritdoc/>
    public void Reconnect()
        => Session.Reconnect();
    /// <inheritdoc/>
    public void Reconnect(DbConnection connection)
        => Session.Reconnect(connection);
    /// <inheritdoc/>
    public DbConnection Close()
        => Session.Close();
    /// <inheritdoc/>
    public void CancelQuery()
        => Session.CancelQuery();
    /// <inheritdoc/>
    public bool IsDirty()
        => Session.IsDirty();
    /// <inheritdoc/>
    public bool IsReadOnly(object entityOrProxy)
        => Session.IsReadOnly(entityOrProxy);
    /// <inheritdoc/>
    public void SetReadOnly(object entityOrProxy, bool readOnly)
        => Session.SetReadOnly(entityOrProxy, readOnly);
    /// <inheritdoc/>
    public object GetIdentifier(object obj)
        => Session.GetIdentifier(obj);
    /// <inheritdoc/>
    public bool Contains(object obj)
        => Session.Contains(obj);
    /// <inheritdoc/>
    public void Evict(object obj)
        => Session.Evict(obj);
    /// <inheritdoc/>
    public object Load(Type theType, object id, LockMode lockMode)
        => Session.Load(theType, id, lockMode);
    /// <inheritdoc/>
    public object Load(string entityName, object id, LockMode lockMode)
        => Session.Load(entityName, id, lockMode);
    /// <inheritdoc/>
    public object Load(Type theType, object id)
        => Session.Load(theType, id);
    /// <inheritdoc/>
    public T Load<T>(object id, LockMode lockMode)
        => Session.Load<T>(id, lockMode);
    /// <inheritdoc/>
    public T Load<T>(object id)
        => Session.Load<T>(id);
    /// <inheritdoc/>
    public object Load(string entityName, object id)
        => Session.Load(entityName, id);
    /// <inheritdoc/>
    public void Load(object obj, object id)
        => Session.Load(obj, id);
    /// <inheritdoc/>
    public void Replicate(object obj, ReplicationMode replicationMode)
        => Session.Replicate(obj, replicationMode);
    /// <inheritdoc/>
    public void Replicate(string entityName, object obj, ReplicationMode replicationMode)
        => Session.Replicate(entityName, obj, replicationMode);
    /// <inheritdoc/>
    public object Save(object obj)
        => Session.Save(obj);
    /// <inheritdoc/>
    public void Save(object obj, object id)
        => Session.Save(obj, id);
    /// <inheritdoc/>
    public object Save(string entityName, object obj)
        => Session.Save(entityName, obj);
    /// <inheritdoc/>
    public void Save(string entityName, object obj, object id)
        => Session.Save(entityName, obj, id);
    /// <inheritdoc/>
    public void SaveOrUpdate(object obj)
        => Session.SaveOrUpdate(obj);
    /// <inheritdoc/>
    public void SaveOrUpdate(string entityName, object obj)
        => Session.SaveOrUpdate(entityName, obj);
    /// <inheritdoc/>
    public void SaveOrUpdate(string entityName, object obj, object id)
        => Session.SaveOrUpdate(entityName, obj, id);
    /// <inheritdoc/>
    public void Update(object obj)
        => Session.Update(obj);
    /// <inheritdoc/>
    public void Update(object obj, object id)
        => Session.Update(obj, id);
    /// <inheritdoc/>
    public void Update(string entityName, object obj)
        => Session.Update(entityName, obj);
    /// <inheritdoc/>
    public void Update(string entityName, object obj, object id)
        => Session.Update(entityName, obj, id);
    /// <inheritdoc/>
    public object Merge(object obj)
        => Session.Merge(obj);
    /// <inheritdoc/>
    public object Merge(string entityName, object obj)
        => Session.Merge(entityName, obj);
    /// <inheritdoc/>
    public T Merge<T>(T entity) where T : class
        => Session.Merge<T>(entity);
    /// <inheritdoc/>
    public T Merge<T>(string entityName, T entity) where T : class
        => Session.Merge<T>(entityName, entity);
    /// <inheritdoc/>
    public void Persist(object obj)
    {
        if (obj is IGenericEntity<Guid> guidEntity)
        {
            guidEntity.Id = generator.Generate<Guid>();
        }
        else if (obj is IGenericEntity<long> longEntity)
        {
            longEntity.Id = generator.Generate<long>();
        }
        else if (obj is IGenericEntity<string> stringEntity)
        {
            stringEntity.Id = generator.Generate<string>();
        }

        Session.Persist(obj);
    }
    /// <inheritdoc/>
    public void Persist(string entityName, object obj)
    {
        if (obj is IGenericEntity<Guid> guidEntity)
        {
            guidEntity.Id = generator.Generate<Guid>();
        }
        else if (obj is IGenericEntity<long> longEntity)
        {
            longEntity.Id = generator.Generate<long>();
        }
        else if (obj is IGenericEntity<string> stringEntity)
        {
            stringEntity.Id = generator.Generate<string>();
        }

        Session.Persist(entityName, obj);
    }
    /// <inheritdoc/>
    public void Delete(object obj)
    {
        if (obj is ISoftDelete softDelete)
        {
            softDelete.Deleted = true;

            Session.Merge(softDelete);
        }
        else
        {
            Session.Delete(obj);
        }
    }
    /// <inheritdoc/>
    public void Delete(string entityName, object obj)
    {
        if (obj is ISoftDelete softDelete)
        {
            softDelete.Deleted = true;

            Session.Merge(entityName, softDelete);
        }
        else
        {
            Session.Delete(entityName, obj);
        }
    }
    /// <inheritdoc/>
    public int Delete(string query)
        => Session.Delete(query);
    /// <inheritdoc/>
    public int Delete(string query, object value, IType type)
        => Session.Delete(query, value, type);
    /// <inheritdoc/>
    public int Delete(string query, object[] values, IType[] types)
        => Session.Delete(query, values, types);
    /// <inheritdoc/>
    public void Lock(object obj, LockMode lockMode)
        => Session.Lock(obj, lockMode);
    /// <inheritdoc/>
    public void Lock(string entityName, object obj, LockMode lockMode)
        => Session.Lock(entityName, obj, lockMode);
    /// <inheritdoc/>
    public void Refresh(object obj)
        => Session.Refresh(obj);
    /// <inheritdoc/>
    public void Refresh(object obj, LockMode lockMode)
        => Session.Refresh(obj, lockMode);
    /// <inheritdoc/>
    public LockMode GetCurrentLockMode(object obj)
        => Session.GetCurrentLockMode(obj);
    /// <inheritdoc/>
    public ITransaction BeginTransaction()
        => Session.BeginTransaction();
    /// <inheritdoc/>
    public ITransaction BeginTransaction(IsolationLevel isolationLevel)
        => Session.BeginTransaction(isolationLevel);
    /// <inheritdoc/>
    public void JoinTransaction()
        => Session.JoinTransaction();
    /// <inheritdoc/>
    public ICriteria CreateCriteria<T>() where T : class
        => Session.CreateCriteria<T>();
    /// <inheritdoc/>
    public ICriteria CreateCriteria<T>(string alias) where T : class
        => Session.CreateCriteria<T>(alias);
    /// <inheritdoc/>
    public ICriteria CreateCriteria(Type persistentClass)
        => Session.CreateCriteria(persistentClass);
    /// <inheritdoc/>
    public ICriteria CreateCriteria(Type persistentClass, string alias)
        => Session.CreateCriteria(persistentClass, alias);
    /// <inheritdoc/>
    public ICriteria CreateCriteria(string entityName)
        => Session.CreateCriteria(entityName);
    /// <inheritdoc/>
    public ICriteria CreateCriteria(string entityName, string alias)
        => Session.CreateCriteria(entityName, alias);
    /// <inheritdoc/>
    public IQueryOver<T, T> QueryOver<T>() where T : class
        => Session.QueryOver<T>();
    /// <inheritdoc/>
    public IQueryOver<T, T> QueryOver<T>(Expression<Func<T>> alias) where T : class
        => Session.QueryOver<T>(alias);
    /// <inheritdoc/>
    public IQueryOver<T, T> QueryOver<T>(string entityName) where T : class
        => Session.QueryOver<T>(entityName);
    /// <inheritdoc/>
    public IQueryOver<T, T> QueryOver<T>(string entityName, Expression<Func<T>> alias) where T : class
        => Session.QueryOver<T>(entityName, alias);
    /// <inheritdoc/>
    public IQuery CreateQuery(string queryString)
        => Session.CreateQuery(queryString);
    /// <inheritdoc/>
    public IQuery CreateFilter(object collection, string queryString)
        => Session.CreateFilter(collection, queryString);
    /// <inheritdoc/>
    public IQuery GetNamedQuery(string queryName)
        => Session.GetNamedQuery(queryName);
    /// <inheritdoc/>
    public ISQLQuery CreateSQLQuery(string queryString)
        => Session.CreateSQLQuery(queryString);
    /// <inheritdoc/>
    public void Clear()
        => Session.Clear();
    /// <inheritdoc/>
    public object Get(Type clazz, object id)
        => Session.Get(clazz, id);
    /// <inheritdoc/>
    public object Get(Type clazz, object id, LockMode lockMode)
        => Session.Get(clazz, id, lockMode);
    /// <inheritdoc/>
    public object Get(string entityName, object id)
        => Session.Get(entityName, id);
    /// <inheritdoc/>
    public T Get<T>(object id)
        => Session.Get<T>(id);
    /// <inheritdoc/>
    public T Get<T>(object id, LockMode lockMode)
        => Session.Get<T>(id, lockMode);
    /// <inheritdoc/>
    public string GetEntityName(object obj)
        => Session.GetEntityName(obj);
    /// <inheritdoc/>
    public IFilter EnableFilter(string filterName)
        => Session.EnableFilter(filterName);
    /// <inheritdoc/>
    public IFilter GetEnabledFilter(string filterName)
        => Session.GetEnabledFilter(filterName);
    /// <inheritdoc/>
    public void DisableFilter(string filterName)
        => Session.DisableFilter(filterName);
    /// <inheritdoc/>
    [Obsolete]
    public IMultiQuery CreateMultiQuery()
        => Session.CreateMultiQuery();
    /// <inheritdoc/>
    public ISession SetBatchSize(int batchSize)
        => Session.SetBatchSize(batchSize);
    /// <inheritdoc/>
    public ISessionImplementor GetSessionImplementation()
        => Session.GetSessionImplementation();
    /// <inheritdoc/>
    [Obsolete]
    public IMultiCriteria CreateMultiCriteria()
        => Session.CreateMultiCriteria();
    /// <inheritdoc/>
    [Obsolete]
    public ISession GetSession(EntityMode entityMode)
        => Session.GetSession(entityMode);
    /// <inheritdoc/>
    public IQueryable<T> Query<T>()
        => Session.Query<T>();
    /// <inheritdoc/>
    public IQueryable<T> Query<T>(string entityName)
        => Session.Query<T>(entityName);
    #endregion implements ISession
    #region implements IDisposableObservable
    /// <inheritdoc/>
    public bool IsDisposed { get; private set; }
    /// <inheritdoc/>
    public void Dispose()
    {
        if (IsDisposed) return;

        IsDisposed = true;
        using var _ = Session;

        GC.SuppressFinalize(this);
    }
    #endregion implements IDisposableObservable


}